use strict;
use warnings;

use ExtUtils::MakeMaker;
use Config;

use Data::Dumper;
use Cwd qw(abs_path);

my $lmcd_src = "src/libmemcached";
my $lmcd_inst = abs_path("src_inst");
my $lmcd_built_lib = "$lmcd_inst/lib/libmemcached$Config{lib_ext}";
my @lmcd_pod = <$lmcd_src/docs/memcached_*.pod>;
my $is_developer = (-d ".svn");

my ($lmcd_h) = eval { build_libmemcached() };
if ($@) {
    warn "Unable to build libmemcached: $@\n";
    warn "Aborted.\n";
    exit 0; # tell cpan testers that this is not a failure
}

my %opts;
if (my $gccversion = $Config{gccversion}) {     # ask gcc to be more pedantic
    print "Your perl was compiled with gcc (version $Config{gccversion}), okay.\n";
    $gccversion =~ s/[^\d\.]//g; # just a number please
    $opts{DEFINE} .= ' -W -Wall -Wpointer-arith -Wbad-function-cast';
    $opts{DEFINE} .= ' -Wno-comment -Wno-sign-compare -Wno-cast-qual';
    $opts{DEFINE} .= ' -Wmissing-noreturn -Wno-unused-parameter' if $gccversion ge "3.0";
    if ($is_developer) {
        #$opts{DEFINE} .= ' -DPERL_GCC_PEDANTIC -ansi -pedantic' if $gccversion ge "3.0";
        $opts{DEFINE} .= ' -Wdisabled-optimization -Wformat'    if $gccversion ge "3.0";
        $opts{DEFINE} .= ' -Wmissing-prototypes';
    }
}

WriteMakefile(
    NAME                => 'Memcached::libmemcached',
    AUTHOR              => 'Tim Bunce <Tim.Bunce@pobox.com>',
    VERSION_FROM        => 'libmemcached.pm',
    ABSTRACT_FROM       => 'libmemcached.pm',
    INC                 => "-I$lmcd_inst/include",
    # We want to link to *our* private libmemcached and not one that
    # might already be installed on the system. The LIBS config gets
    # appended to the link command line, so if we used "-L$dir -lmemcached"
    # then the installed lib would get preference over ours.
    # So we explicitly refer to our static library. That also avoids the
    # need to worry about what library might get used at runtime.
    LDFROM           => '$(OBJECT)'." $lmcd_built_lib",
    PREREQ_PM           => {
        perl         => 5.006,
        'Test::More' => 0,
    },
    dist                => {
        COMPRESS => 'gzip -9f', SUFFIX => 'gz',
        DIST_DEFAULT=> 'clean distcheck disttest tardist',
        PREOP => '$(MAKE) -f Makefile.old distdir',
    },
    # see also MY::postamble below
    clean               => {
        FILES => 'Memcached-libmemcached-* lib/Memcached/libmemcached/*_hash.pl',
    },
    %opts,
);


sub MY::postamble {
return qq{
LMCD_SRC=$lmcd_src
LMCD_INST=$lmcd_inst
LMCD_BUILT_LIB=$lmcd_built_lib
}.q{
clean ::

realclean ::
	$(RM_RF) $(LMCD_INST)
	cd $(LMCD_SRC) && $(MAKE) distclean

$(OBJECT) : $(LMCD_BUILT_LIB)

svnmanifest::
	svn list -R | grep -v '/$$' > MANIFEST
	svn diff MANIFEST
	svn status
}
}



exit 0;


sub run {
    my ($cmd) = @_;
    warn "$cmd\n";
    system($cmd) == 0
        or die "Error running $cmd\n";
}

sub build_libmemcached {
    sync_libmemcached_pod();
    extract_libmemcached_functions();
    extract_libmemcached_constants();

    return if -d "$lmcd_inst/lib"; # XXX assume it built ok. use 'make realclean' to rm
    mkdir $lmcd_inst unless -d $lmcd_inst;
    run("cd $lmcd_src && make distclean") if -f "$lmcd_src/Makefile";
    my $configure_args = '';#'--disable-shared';
    if ($is_developer) {    # XXX make a Makefile.PL argument/option
        $configure_args .= ' --with-pic';
        #$configure_args .= ' --enable-debug';
        warn "libmemcached configure $configure_args";
    }
    run("cd $lmcd_src && ./configure $configure_args --prefix=$lmcd_inst");
    run("cd $lmcd_src && make install");
}

sub sync_libmemcached_pod {
    return unless -d ".svn";
    # we duplicate the libmemcached pod in svn so that the docs can be read on search.cpan.org
    my $perl_pod_dir = "lib/Memcached/libmemcached";
    for my $src_pod (@lmcd_pod) {
        (my $dst_pod = $src_pod) =~ s!$lmcd_src/docs!$perl_pod_dir!;
        run("cp $src_pod $dst_pod && svn add -q $dst_pod");
    }
    # XXX svn rm any $perl_pod_dir/memcached_*.pod that weren't in @lmcd_pod
}

sub extract_libmemcached_functions {
    my %libmemcached_func;

    # find all memcached_* functions
    warn "Reading libmemcached pod docs to find all public functions\n";
    for my $src_pod (@lmcd_pod) {
        open my $fh, "<$src_pod" or die "Can't open $src_pod: $!";
        while (<$fh>) {
            next unless /\b(memcached_\w+)\s*\(/;
            $libmemcached_func{$1} = 1;
        }
    }

    # write 
    my $func_pm = "lib/Memcached/libmemcached/func_hash.pl";
    warn "Writing $func_pm\n";
    open my $func_pm_fh, ">$func_pm" or die "Can't open $func_pm: $!";
    local $\ = "\n";
    print $func_pm_fh "# DO NOT EDIT! GENERATED BY $0 ON ".localtime(time)."\n";
    print $func_pm_fh Data::Dumper->Dump([\%libmemcached_func], [qw(libmemcached_func)]);
    close $func_pm_fh or die "Error closing $func_pm: $!";

    # sanity check the generated file
    my $loaded = require $func_pm;
    die "$func_pm didn't return a HASH reference ($loaded)"
        unless ref $loaded eq 'HASH';
}

sub extract_libmemcached_constants {
    my %libmemcached_const;

    # find all MEMCACHED_* constants (#define and enum)
    warn "Reading memcached.h to find all constants\n";
    open my $fh, "<$lmcd_src/include/memcached.h" or die "Can't open $lmcd_src/include/memcached.h: $!";
    my $in_enum = 0;
    my @const;
    while (<$fh>) {
        if ($in_enum) {
            if (m/^ \s* } \s* (\w+)/x) {
                $libmemcached_const{$_} = $1 for @const;
                @const = ();
                $in_enum = 0;
            }
            elsif (m/^ \s* (MEMCACHED_\w+)/x) {
                push @const, $1;
            }
        }
        elsif (m/^ \s* typedef \s+ enum /x) {
            $in_enum = 1;
        }
        elsif (m/\# \s* define \s+ (MEMCACHED_\w+)/x) {
            $libmemcached_const{$1} = 1;
        }
    }

    # write raw hash of const names
    my $const_pm = "lib/Memcached/libmemcached/const_hash.pl";
    warn "Writing $const_pm\n";
    open my $const_pm_fh, ">$const_pm" or die "Can't open $const_pm: $!";
    local $\ = "\n";
    print $const_pm_fh "# DO NOT EDIT! GENERATED BY $0 ON ".localtime(time)."\n";
    print $const_pm_fh Data::Dumper->Dump([\%libmemcached_const], [qw(libmemcached_const)]);
    close $const_pm_fh or die "Error closing $const_pm: $!";

    # sanity check the generated file
    my $loaded = require $const_pm;
    die "$const_pm didn't return a HASH reference ($loaded)"
        unless ref $loaded eq 'HASH';

    # write raw hash of const names
    my $const_xs = "const-xs.inc";
    warn "Writing $const_xs\n";
    open my $const_xs_fh, ">$const_xs" or die "Can't open $const_xs: $!";
    local $\ = "\n";
    print $const_xs_fh "# DO NOT EDIT! GENERATED BY $0 ON ".localtime(time)."\n";
    print $const_xs_fh "IV\nconstant()";
    print $const_xs_fh "\tALIAS:";
    print $const_xs_fh "\t$_ = $_" for sort keys %libmemcached_const;
    print $const_xs_fh "\tCODE:";
    print $const_xs_fh "\tRETVAL = ix;";
    print $const_xs_fh "\tOUTPUT:";
    print $const_xs_fh "\tRETVAL";
    close $const_xs_fh or die "Error closing $const_xs: $!";
}

